;本代码为抢答器改进版
;改进如下:
;1. 8255的C端做输入C0接一个电平开关,C1~C7接低电平 A端做输出接数码管
;2. 由用户键盘按下0到9 做抢答,其他键无效(此处无效不做任何回响)
;   按q键 退出程序
;3. 电平开关作为Enable控制 低电平抢答按键无效,高电平有效
;4. 电平开关变化时 将通知用户抢答器启用/禁用 
;   抢答器禁用时 用户按下键 程序将输出提醒用户 当前抢答器不可用
;5. 抢答触发后数码管显示编号数字 同时主机响铃

DATA SEGMENT
;-接口区-
IO8255A EQU 288H              ;8255 PA口
IO8255C EQU 28AH              ;8255 PC口
IO8255D EQU 28BH              ;8255 控制端口 
;-数码表- 数码表对应详见文件尾部
LED DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,67H 
;-提示信息区-
MS1 DB 0AH,0DH,'Press 0~9 to answer and q to exit!',0AH,0DH,'$'
MS2 DB 0AH,0DH,'Disable answering machine!',0AH,0DH,'$'
MS3 DB 0AH,0DH,'Enable answering machine!',0AH,0DH,'$'
MS4 DB 0AH,0DH,'Answering machine is disable now!',0AH,0DH,'$'
MS5 DB 0AH,0DH,'Please turn ofF the box and computer !',0AH,0DH,'$'
MS6 DB 0AH,0DH,'Press q to exit or others to continue!',0AH,0DH,'$'
DATA ENDS

CODE SEGMENT
ASSUME CS:CODE,DS:DATA 
START:
;初始化设置
      ;数据段初始化
      MOV AX,DATA 
      MOV DS,AX     
      ;向8255控制端口写89H(1000 1001) 
      ;(1)特征位 (00)A方式0 (0)A输出 
      ;(1)C高4位输入 (0)B方式0 (0)B输出 (1)C低4位输入
      ;即C输入 A B输出 工作方式0
      MOV DX,IO8255D
      MOV AL,89H
      OUT DX,AL

      ;从8255C口读出控制开关状态到AL 再转存到BL中
STT:   MOV DX,IO8255C 
      IN AL,DX 
      MOV BL,AL   ;此处BL保存的初始开关状态  

      ;输出提示信息:
BG:   LEA DX,MS1
      MOV AH,9
      INT 21H      
;TESTED 循环检测抢答器状态
        ;从8255C口读出控制开关状态到AL
TESTED: MOV DX,IO8255C 
        IN AL,DX 
        CMP AL,BL 
        ;将当前的开关状态(AL)和上次的开关状态(BL)做比较
        JB L1      ;AL若大于BL 则说明AL(1) BL(0) 开关变为开启
        JA L2      ;AL若小于BL 则说明AL(0) BL(1) 开关变为禁止 
        JMP L3     ;没改变直接跳L3
;L1 提示用户 开关状态变为开启        
L1:     PUSH AX
        LEA DX,MS3
        MOV AH,9
        INT 21H
        POP AX
        JMP L3
;L2 提示用户 开关状态变为禁止
L2:     PUSH AX
        LEA DX,MS2 
        MOV AH,9
        INT 21H
        POP AX    
;L3 更新BL并检测是否有用户输入
L3:     MOV BL,AL      ;更新BL
        MOV DL,0FFH    ;检测输入 
        MOV AH,6       ;6号功能
        INT 21H        ;从缓冲区中读用户输入到AL
        JNZ NEXT       ;AL不为0 有输入 跳转到按键检测
        JMP TESTED  ;若用户无输入 则继续读开关状态 

      ;根据按键选择不同分支
NEXT: CMP AL,'q'
      JZ EXIT     ;等于q 退出程序
      CMP AL,'9' 
      JA TESTED      ;比9大 直接不响应
      CMP AL,'0'
      JB TESTED      ;比0小 直接不响应
      ;以下是0~9的处理
      PUSH AX     ;保存AL的值
      ;从8255的C端读入(电平开关)输入的值到AL 非0即1
      MOV DX,IO8255C 
      IN AL,DX 
      ;判断此时使能开关是开启还是关闭
      CMP AL,0    
      JNZ ENAB  ;不等于0 说明是开启状态 跳至ENAB 显示抢答
      LEA DX,MS4 ;等于0 说明是禁用状态 输出提示用户 当前禁用
      MOV AH,9       
      INT 21H 
      POP AX      ;两个分支的AX都需要出栈
      JMP BG     ;跳至BG 输出提示信息 并重新等待用户按键

;若当前使能 则让数码管显示
ENAB: POP AX      ;取出用户按键的ASCII码 AL
      MOV BX,OFFSET LED ; 数码表的入口偏移给BX
      MOV DL,30H        
      SUB AL,DL         ;ASCII转数字
      XLAT              ;将DS:[BX+AL]处的一个字节送入AL
      ;将AL中的数码写入8255的A端 送给数码管
      MOV DX,IO8255A    
      OUT DX,AL 
      ;输出响铃符 制造响铃效果 (字符图案为一个实心圆)
      MOV DL,7          ;响铃符的ASCII为7
      MOV AH,2 
      INT 21H 
      LEA DX,MS6        ;询问用户是否返回菜单继续或退出
      MOV AH,9
      INT 21H 
      MOV AH,8          ;等待用户按键(此处的等待是绝对占用的)
      INT 21H
      CMP AL,'q'  
      JZ EXIT           ;按下q键退出
      JMP STT            ;按下其他键 则回到菜单



;EEE结束程序
EXIT: LEA DX,MS5
      MOV AH,9
      INT 21H     ;打印退出提示信息
      MOV AH,4CH 
      INT 21H  
CODE ENDS
END START


;数码表说明      
;     h g f e d c b a  字符
; 3FH(0 0 1 1 1 1 1 1)  0
; 06H(0 0 0 0 0 1 1 0)  1 
; 5BH(0 1 0 1 1 0 1 1)  2
; 4FH(0 1 0 0 1 1 1 1)  3
; 66H(0 1 1 0 0 1 1 0)  4
; 6DH(0 1 1 0 1 1 0 1)  5
; 7DH(0 1 1 1 1 1 0 1)  6
; 07H(0 0 0 0 0 1 1 1)  7
; 7FH(0 1 1 1 1 1 1 1)  8
; 67H(0 1 1 0 0 1 1 1)  9
